package com.foreveross.crawl.adapter.sub.impl20140402.v3;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import com.foreveross.crawl.adapter.AbstractAdapter;
import com.foreveross.crawl.adapter.PlaneInfoEntityBuilder;
import com.foreveross.crawl.domain.airfreight.CabinEntity;
import com.foreveross.crawl.domain.airfreight.single.SinglePlaneInfoEntity;
import com.foreveross.proxyip.ProxyipProperties;
import com.foreveross.taskservice.common.bean.TaskModel;
import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.Page;
import com.gargoylesoftware.htmlunit.ProxyConfig;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.WebRequest;
import com.gargoylesoftware.htmlunit.WebResponse;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.util.NameValuePair;
import com.google.common.io.ByteStreams;

public class SingaporeAirAdapter extends AbstractAdapter{

	//舱位映射数据
	private Map<String,String> cabinRequestMap=null;
	
	private WebClient globalWebClient=null;
	public SingaporeAirAdapter(TaskModel taskQueue) {
		super(taskQueue);
		// TODO Auto-generated constructor stub
	}

	@Override
	public List<Object> paraseToVo(Object fetchObject) throws Exception {
		// TODO Auto-generated method stub
		List<Object> result=(List<Object>)fetchObject;
		return result;
	}

	@Override
	public String getUrl() throws Exception {
		return null;
		
	}

	@Override
	public Object fetch(String url) throws Exception {
		try {
			
		switch (getRouteType()) {
        case DOMESTIC_ONEWAYTRIP:
        case INTERNATIONAL_ONEWAY:
        	return fetchOnewayTripByWebClient();
        default:
        	return null;
       }
		
		} catch (Throwable e) {
			// TODO Auto-generated catch block
			throw new Exception(e);
		}
	}
	
	/**
	 * 获取动态生成的订票查询URL
	 * @param html
	 * @return
	 * @throws IOException
	 */
	private String fetchActionFromHtml(String html){//USE JSOUP ?
		int index=html.indexOf("searchForm");
		String subHtml=new String(html.substring(index));
		subHtml=subHtml.substring(subHtml.indexOf("action=\"")+"action=\"".length());
		String result=new String(subHtml.substring(0,subHtml.indexOf("\"")));
		return result;
	}
	
	/**
	 * 按任务的舱位类型等预处理参数
	 * @param taskQueue
	 * @param cabinClass
	 * @return
	 */
	private Map<String, String> prepareParams(TaskModel taskQueue,String cabinClass){
		 Map<String, String> params=new HashMap<String, String>();
	     params.put("fromHomePage","true");
	     params.put("_payByMiles","on");
	     params.put("_pwmFlightSearchCheckBox","on");
	     params.put("recentSearches","");
	     params.put("origin",taskQueue.getFromCity());
	     params.put("destination",taskQueue.getToCity());
	     String[] tmpArr=taskQueue.getFlightDate().split("-");
	     params.put("departureDay",tmpArr[2]);//
	     params.put("departureMonth",tmpArr[1]+tmpArr[0]);
	     params.put("_tripType","on");
	     
	     if(taskQueue.getIsReturn()==0){
	    	 params.put("tripType","O");
	     }
	     if(taskQueue.getIsReturn()==1){
	    	 tmpArr=taskQueue.getReturnGrabDate().split("-");
	    	 params.put("returnDay",tmpArr[2]);
	    	 params.put("returnMonth",tmpArr[1]+tmpArr[0]);
	     }
	     
	     params.put("cabinClass",cabinClass);
	     params.put("numOfAdults","1");
	     params.put("numOfChildren","0");
	     params.put("numOfInfants","0");
	     params.put("_eventId_flightSearchEvent","");
	     params.put("numOfChildNominees","");
	     params.put("numOfAdultNominees","");
	     params.put("isLoggedInUser", "false");
	   
	     return params;
	}
	
	/**
	 * 设置查询航班价格信息的参数
	 * @param priceInfoKey
	 * @return
	 */
	private Map<String, String> prepareParams(String priceInfoKey){
		Map<String, String> params=new HashMap<String, String>();
		params.put("hid_flightIDs", "");
		params.put("hid_recommIDs", "");
		params.put("tripType", "O");
		params.put("userPreferedCurrency", "");
		params.put("selectedFlightIdDetails[0]", priceInfoKey);
		return params;
	}
	
	/**
	 * 为请求设置参数
	 * @param params
	 * @param webRequest
	 */
	private void prepareParasForRequest(Map<String, String> params,WebRequest webRequest){
		List<NameValuePair> requestParameters=new ArrayList<NameValuePair>();
		if (params != null && params.size() > 0) {  
		    for (Entry<String, String> param : params.entrySet()) {  
		    	requestParameters.add(new NameValuePair(param.getKey(), param.getValue()));  
		    }  
		}  
		webRequest.setRequestParameters(requestParameters);
	}
	
	/**
	 * 获取subCabinName列表
	 * @param doc
	 * @return
	 */
	private List<String> fetchSubCabinNameList(Document doc) throws Throwable{
//		String s=FileUtil.readString("C:\\Users\\jake\\Desktop\\foss\\选择航班——付费预订_部份不可用.htm","Unicode");
//	    doc = Jsoup.parse(s, "http://www.singaporeair.com/");
		Elements subCabinNameEles=doc.select("th.jQpromotionOptions").select("a");
		if(subCabinNameEles.isEmpty()){
			throw new Exception("no subCabin exist");
		}
		List<String> subCabinNameList=new ArrayList<String>();
		for(Element e:subCabinNameEles){
			subCabinNameList.add(e.ownText());
		}
		return subCabinNameList;
	}
	
	/**
	 * 解析单程航班信息实体，获得航班信息，及舱位价格信息
	 * @param result
	 * @param html
	 * @param cabinClassKey
	 * @return
	 * @throws Throwable
	 */
	private List<SinglePlaneInfoEntity> fetchSinglePlaneInfo(List<SinglePlaneInfoEntity> result,String html,String cabinClassKey) throws Throwable{
   	 
		//String s=FileUtil.readString("d:/ret.html");
		//String s=FileUtil.readString("C:\\Users\\jake\\Desktop\\foss\\订位_行程无航线价格.htm","Unicode");
		Document doc = Jsoup.parse(html, "http://www.singaporeair.com/");
		Elements alertEles=doc.select("div[class^=alertBox]");
		if(!alertEles.isEmpty()){
			String alertMsg=alertEles.first().select("div.alertMsg").text();
			throw new Exception("alert: "+alertMsg);
		}
		//获取subCabinName列表
		List<String> subCabinNameList=fetchSubCabinNameList(doc);
		
		Elements flightEles=doc.getElementsByClass("jQflight");//获得所有单程航班信息html
		if(flightEles.isEmpty()){
			throw new Exception("request url invalidate,retry it with another proxy------");
		}
		
		for(Element e:flightEles){
			Elements node=e.select("li:not(.divider)");//获得某一航班的各个具体结点信息
			String flightNo=node.first().select("em").text();
			String flightType=node.first().select("span").first().text();
			
			Elements tmpEle=node.get(1).select("span");
			String tmpStartDate=tmpEle.first().child(0).text();
			String tmpStartTime=tmpEle.first().ownText();
			
			tmpEle=node.get(2).select("span");
			String tmpEndDate=tmpEle.first().child(0).text();
			String tmpEndTime=tmpEle.first().ownText();
			
			String costTime=node.get(3).ownText();
			logger.debug("--------"+Arrays.toString(new Object[]{flightNo,flightType,tmpStartDate,tmpStartTime,tmpEndDate,tmpEndTime,costTime}));
			SinglePlaneInfoEntity planeInfo = PlaneInfoEntityBuilder.buildPlaneInfo(
					taskQueue, "SQ", "新航", "新加坡航空有限公司", tmpStartTime, tmpEndTime, flightNo, "", null, "", "", SinglePlaneInfoEntity.class);
			

    		String queryPriceUrl="http://www.singaporeair.com/chooseFlightJson.form"; 
    		WebRequest webRequest = new WebRequest(new URL(queryPriceUrl)); 
	    	webRequest.setHttpMethod(com.gargoylesoftware.htmlunit.HttpMethod.GET); 
	    	
	    	 //如果己存在航班信息，则取出来设置舱位信息
		    SinglePlaneInfoEntity planeInfoEntity=containAndGetSinglePlaneInfoEntity(result, planeInfo);
		    
		    //设置舱位数据
		    Set<CabinEntity> cabins=planeInfoEntity.getCabins();
    		if(null==cabins){
    			cabins=new HashSet<CabinEntity>();
    		}
			
			Elements chooseFlightSelectionEles=e.select("td[class^=chooseFlightSelection]");
			//assert chooseFlightSelectionEles.size==subCabinNameList.size
			for(int i=0;i<chooseFlightSelectionEles.size();i++){
				if(!chooseFlightSelectionEles.get(i).className().equals("chooseFlightSelection")){
					logger.debug("航班-时间段－舱位－子舱位－chooseFlightSelection notAvailable");
					continue;
				}
				
				//subCabinNameList deal
				String priceInfoKey=chooseFlightSelectionEles.get(i).select("input").val();
		    	Map<String,String> priceQueryPara=prepareParams(priceInfoKey);
		    	prepareParasForRequest(priceQueryPara, webRequest);
		    	String msg=sendRequest(getGlobalWebClient(),webRequest);
		    	if(logger.isDebugEnabled()){
		    		logger.debug("price json data with priceInfoKey["+priceInfoKey+"]:\r\n"+msg);
		    	}
		    	String price=fetchPriceFromJsonData(msg);//get price
	    		CabinEntity cabin = PlaneInfoEntityBuilder.buildCabinInfo(cabinClassKey,subCabinNameList.get(i),
	    				cabinRequestMap.get(cabinClassKey), price, price, "", "", CabinEntity.class);
	    		cabins.add(cabin);
			}
	    	
    		planeInfoEntity.setCabins(cabins);
		}
		return result;
	}
	
	/**
	 * 从html页面上取出json字符串，并解析出json中的价格信息
	 * @param jsonStr
	 * @return
	 * @throws Throwable
	 */
	private static String fetchPriceFromJsonData(String jsonStr) throws Throwable{
		//String s=FileUtil.readString("C:\\Users\\jake\\Desktop\\foss\\price.json");
		//jsonStr=FileUtil.readString("d:/jsonhtml.txt");
		Document doc = Jsoup.parse(jsonStr, "http://www.singaporeair.com/");
		jsonStr=doc.select("body").text().trim();
		JSONObject jsonObject = JSONObject.fromObject(jsonStr);
		String price=jsonObject.getJSONObject("price").getString("unFormattedTotal");
		return price;
	}

	public List<SinglePlaneInfoEntity> fetchOnewayTripByWebClient() throws Throwable{
		//0.获取WebClient并执行
		WebClient webClient=getGlobalWebClient();
		String url="http://www.singaporeair.com/";
	    HtmlPage htmlPage = webClient.getPage(url);  
	    webClient.waitForBackgroundJavaScript(30000);  // 等待JS驱动dom完成获得还原后的网页
	    String html=htmlPage.asXml();
	    if(logger.isDebugEnabled()){
        	//logger.debug("url["+url+"] finally response is:"+html);
	    	//Set<Cookie> cookies=webClient.getCookieManager().getCookies();
	    	//logger.debug("cookies: "+Arrays.toString(cookies.toArray()));
        }
	    
	    //1.获取舱位映射数据Map<Y,"经济舱">
	    cabinRequestMap=prepareCabinArgsMap(html);
//	    cabinRequestMap.remove("J");
//	    cabinRequestMap.remove("P");
	    
	    //2.获取订票URL
	    String bookingUrl="http://www.singaporeair.com"+fetchActionFromHtml(html);
	    //多次抓取后可能出现需要重新请求以获取booking url的情况
	    if(bookingUrl.endsWith("e1s1")){
	    	Map<String, String> params=prepareParams(taskQueue,"");
	    	WebRequest webRequest = new WebRequest(new URL(bookingUrl)); 
	    	webRequest.setHttpMethod(com.gargoylesoftware.htmlunit.HttpMethod.POST);  
	    	prepareParasForRequest(params, webRequest);
	    	String msg=sendRequest(webClient,webRequest);
	    	bookingUrl="http://www.singaporeair.com"+fetchActionFromHtml(msg);
	    }
	   //返回结果模型
		List<SinglePlaneInfoEntity> result=new ArrayList<SinglePlaneInfoEntity>();
		
		//遍历舱位类型
	    for(String cabinClassKey:cabinRequestMap.keySet()){
	    	//3.设置请求参数
	    	Map<String, String> params=prepareParams(taskQueue,cabinClassKey);
	    	WebRequest webRequest = new WebRequest(new URL(bookingUrl)); 
	    	webRequest.setHttpMethod(com.gargoylesoftware.htmlunit.HttpMethod.POST);  
	    	prepareParasForRequest(params, webRequest);
	    	
	    	//4.执行获得行程航班信息结果
	    	String msg=sendRequest(webClient,webRequest);
	    	
	    	//5.解析出航班信息
	    	fetchSinglePlaneInfo(result, msg,cabinClassKey);
	    	
	    }
	    //释放资源
	    webClient.closeAllWindows();  
	    return result;
	}
	
   /**
    * 如果己存在此单程航班主体信息，则从LIST结果中返回，否则返回它本身(在同一个TaskModel作用域下)	
    * @param result
    * @param planeInfo
    * @return
    */
   private SinglePlaneInfoEntity containAndGetSinglePlaneInfoEntity(List<SinglePlaneInfoEntity> result,SinglePlaneInfoEntity planeInfo){
	   for(SinglePlaneInfoEntity e:result){
		   boolean validate=true;
		   validate&=(e.getFlightNo().equals(planeInfo.getFlightNo()) && e.getStartTime().equals(planeInfo.getStartTime()) && e.getEndTime().equals(planeInfo.getEndTime()));
		   if(validate)
			   return e;
	   }
	   result.add(planeInfo);
	   return planeInfo;
   }
      
    //底层请求  
    private String sendRequest(WebClient webClient,WebRequest webRequest) throws Exception{  
        String retMsg=null;
    	byte[] responseContent = null;  
        Page page = webClient.getPage(webRequest);  
        WebResponse webResponse = page.getWebResponse();  
        int status = webResponse.getStatusCode();  
        // 读取数据内容  
        if (status==200) {  
            if (page.isHtmlPage()) {  
                 webClient.waitForBackgroundJavaScript(30000);
                 responseContent = ((HtmlPage) page).asXml().getBytes();
            } else {  
                InputStream bodyStream = webResponse.getContentAsStream();  
                responseContent = ByteStreams.toByteArray(bodyStream);  
                bodyStream.close();  
            }  
            retMsg=new String(responseContent);
        }
        // 关闭响应流  
        webResponse.cleanUp();  
        
        if(logger.isDebugEnabled()){
        	logger.debug("Charset : " + webResponse.getContentCharset());
        	logger.debug("ContentType : " + webResponse.getContentType());
        	//logger.debug("Content : "+retMsg);
        }
      
        return retMsg;  
    }  
	 /**
     * must override this method, the AbstractAdapter using this.getHttpClient() to create globleHttpClient().
     * ugly AbstractAdapter definition.
     *
     * @return
     * @throws Exception
     */
    public WebClient getGlobalWebClient() throws Exception {
        if (globalWebClient == null) {
        	globalWebClient = getWebClient();
        }/*
        logger.info(String.format("[%s] 调用全局HttpClient",taskQueue.getGrabChannel()));*/
        return globalWebClient;
    }
	
    public WebClient getWebClient() {
    	globalWebClient=super.getWebClient();
    	//0.设置代理
    	if (ProxyipProperties.isProxyEnable()) {
    		if (proxyIp != null) {
    			ProxyConfig proxyConfig = globalWebClient.getOptions().getProxyConfig();  
    		    proxyConfig.setProxyHost(proxyIp.getIp());
    		    proxyConfig.setProxyPort(proxyIp.getPort()); 
    			
    		}
    	}
    	
    	//1.设置webclient常用参数
    	globalWebClient.getOptions().setJavaScriptEnabled(true);
    	globalWebClient.getOptions().setCssEnabled(false);  
    	globalWebClient.getOptions().setRedirectEnabled(true); 
    	globalWebClient.getOptions().setThrowExceptionOnScriptError(false);  
    	globalWebClient.getOptions().setTimeout(30000); 
    	//SO_TIMEOUT
        return globalWebClient;
    }
    
	@Override
	public boolean validateFetch(Object fetchObject) throws Exception {
		// TODO Auto-generated method stub
		return true;
	}

	/**
	 * 获取舱位映射数据<Y,经济舱>
	 * @param html
	 * @return
	 * @throws IOException
	 */
	private static Map<String,String> prepareCabinArgsMap(String html) throws IOException{
		Map<String,String> cabinMap=new HashMap<String, String>();
		//html=FileUtil.readString("C:\\Users\\jake\\Desktop\\foss\\欢迎访问新加坡航空公司中文官方网站.htm","Unicode");
		Document doc = Jsoup.parse(html, "http://www.singaporeair.com/");
        Element selectElement=doc.getElementById("cabinClass");
        for(Element optionElement:selectElement.children()){
        	cabinMap.put(optionElement.val(), optionElement.text());
        }
		return cabinMap;
	}
	
	public static void main(String[] args) throws Throwable {
    	
        TaskModel taskModel = new TaskModel();
        taskModel.setIsInternational(1);//国际单程
        taskModel.setIsReturn(0);
        taskModel.setFromCity("PVG");
        taskModel.setFromCityName("上海");
        taskModel.setToCity("SIN");
        taskModel.setToCityName("新加坡");
        taskModel.setFlightDate("2014-07-14");
       // taskModel.setFlightDate("2014-07-01"); 部份可用
       // taskModel.setFlightDate("2014-06-28"); 无座位航班
       // taskModel.setFlightDate("2014-06-30"); 单舱位子类
       // taskModel.setGrabChannelId(2L);
       
        SingaporeAirAdapter adapter = new SingaporeAirAdapter(taskModel);
       // adapter.setProxyIp(new ProxyIpModel("127.0.0.1", 7777));
     //   adapter.setProxyIp(new ProxyIpModel("182.124.229.180", 8088));
//        String newIp=adapter.getNewIp().getProxyIp();
//        System.out.println("new ip:"+newIp);
        
     //  adapter.setProxyIp(adapter.getNewIp());
        List list =  adapter.paraseToVo(adapter.fetch(null));
        JSONArray jsonarray = JSONArray.fromObject(list); 
		String s=jsonarray.toString();
		System.out.println(s);
 

	}
}
